using System;
using System.Collections.Generic;
using System.Linq;
using System.Transactions;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using DotNetOpenAuth.AspNet;
using Microsoft.Web.WebPages.OAuth;
using WebMatrix.WebData;
using T2.WebSecurity.UI.MVC.Filters;
using T2.WebSecurity.UI.MVC.Models;
using T2.WebSecurity.Common.Log;
using T2.WebSecurity.Common;
using System.Net.Http;
using System.Net;
using whttp = System.Web.Http;
using T2.WebSecurity.Business.Business;
using T2.WebSecurity.Business.Entities.Interfaces;


namespace T2.WebSecurity.UI.MVC.Controllers
{
    [Authorize]
    [InitializeSimpleMembership]
     public partial class AccountController : Controller
    {
        //
        // GET: /Account/Login

        [AllowAnonymous]
        public ActionResult Login(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            return View();
        }

        //
        // POST: /Account/Login

        [HttpPost]
        [AllowAnonymous]
        public ActionResult Login(LoginModel model, string returnUrl)
        {
            if (ModelState.IsValid && WebMatrix.WebData.WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
            {
                return RedirectToLocal(returnUrl);
            }

            return new HttpStatusCodeResult(403, "The user name or password provided is incorrect.");
        }

        //
        // POST: /Account/LogOff

        [HttpPost]
        public ActionResult LogOff()
        {
            WebMatrix.WebData.WebSecurity.Logout();

            return RedirectToAction("Index", "Home");
        }

        //
        // GET: /Account/Register

        [AllowAnonymous]
        public ActionResult Register()
        {
            RegisterModel model = new RegisterModel();
            return View(model);
        }

        //
        // POST: /Account/Register

        [HttpPost]
        [AllowAnonymous]
        public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                // Attempt to register the user
                try
                {
                    string confirmString = WebMatrix.WebData.WebSecurity.CreateUserAndAccount(model.UserName, model.Password, new{FirstName = model.FirstName, 
                        LastName = model.LastName, Email = model.Email, Address= model.Address, City = model.City, Region = model.Region, 
                        PostalCode = model.PostalCode, OfficePhone = model.OfficePhone, CellPhone = model.CellPhone, Title = model.Title, 
                        PasswordQuestion = model.PasswordQuestion, PasswordAnswer = model.PasswordAnswer, IsApproved = "false"}, requireConfirmationToken:true);

                    if (!confirmString.Trim().Equals(""))
                    {
                        //Send email activation link
                        EmailService emailService = new EmailService();
                        emailService.SendAccountConfirmEmail(model.Email, confirmString);
                    }

                    //WebMatrix.WebData.WebSecurity.Login(model.UserName, model.Password);
                    return RedirectToAction("Index", "Home");
                }
                catch (MembershipCreateUserException e)
                {
                    ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        //
        // POST: /Account/Disassociate

        [HttpPost]
        public ActionResult Disassociate(string provider, string providerUserId)
        {
            string ownerAccount = OAuthWebSecurity.GetUserName(provider, providerUserId);
            ManageMessageId? message = null;

            // Only disassociate the account if the currently logged in user is the owner
            if (ownerAccount == User.Identity.Name)
            {
                // Use a transaction to prevent the user from deleting their last login credential
                using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.Serializable }))
                {
                    bool hasLocalAccount = OAuthWebSecurity.HasLocalAccount(WebMatrix.WebData.WebSecurity.GetUserId(User.Identity.Name));
                    if (hasLocalAccount || OAuthWebSecurity.GetAccountsFromUserName(User.Identity.Name).Count > 1)
                    {
                        OAuthWebSecurity.DeleteAccount(provider, providerUserId);
                        scope.Complete();
                        message = ManageMessageId.RemoveLoginSuccess;
                    }
                }
            }

            return RedirectToAction("Manage", new { Message = message });
        }

        //
        // GET: /Account/Manage

        public ActionResult Manage(ManageMessageId? message)
        {
            return View("ChangePassword");
        }

        //
        // POST: /Account/Manage

        [HttpPost]
        public ActionResult Manage(LocalPasswordModel model)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    // ChangePassword will throw an exception rather than return false in certain failure scenarios.
                    bool changePasswordSucceeded;
                    try
                    {
                        changePasswordSucceeded = WebMatrix.WebData.WebSecurity.ChangePassword(User.Identity.Name, model.OldPassword, model.NewPassword);
                        //SimpleMembershipProvider myMemberProvider = new SimpleMembershipProvider();
                        //changePasswordSucceeded = myMemberProvider.ChangePassword(User.Identity.Name, model.OldPassword, model.NewPassword);
                    }
                    catch (Exception)
                    {
                        changePasswordSucceeded = false;
                    }

                    if (changePasswordSucceeded)
                    {
                        MyLogger.MyLog("T2.Medussa", "MVC4", "Password Changed for " + User.Identity.Name, MyEnum.LogCategories.Error, 10, 7010, System.Diagnostics.TraceEventType.Information, "Password Changed for " + User.Identity.Name);
                        HttpResponseMessage message = new HttpResponseMessage();
                        message.ReasonPhrase = "Password has  been changed";
                        message.StatusCode = HttpStatusCode.OK;
                        throw new whttp.HttpResponseException(message);
                    }
                    else
                    {
                        MyLogger.MyLog("T2.Medussa", "MVC4", "Password Could Not Be Changed for " + User.Identity.Name, MyEnum.LogCategories.Error, 10, 7010, System.Diagnostics.TraceEventType.Information, "Password Changed for " + User.Identity.Name);
                        HttpResponseMessage message = new HttpResponseMessage();
                        message.ReasonPhrase = "Password Could Not Be Changed";
                        message.StatusCode = HttpStatusCode.NotAcceptable;
                        throw new whttp.HttpResponseException(message);
                    }
                }
            }
            catch (Exception ex)
            {
                MyLogger.MyLog("T2.Medussa", "MVC4", "Error Occurred in Manage Change Password", MyEnum.LogCategories.Error, 10, 7010, System.Diagnostics.TraceEventType.Information, ex.Message + ex.StackTrace);
                HttpResponseMessage message = new HttpResponseMessage();
                message.ReasonPhrase = "Errors have occured the system admin has been notified.";
                message.StatusCode = HttpStatusCode.InternalServerError;
                throw new whttp.HttpResponseException(message);
            }

            MyLogger.MyLog("T2.Medussa", "MVC4", "Bad change password attempt", MyEnum.LogCategories.Error, 10, 7010, System.Diagnostics.TraceEventType.Information, "Bad change password attempt");
            HttpResponseMessage message1 = new HttpResponseMessage();
            message1.ReasonPhrase = "Errors in your input please try again.";
            message1.StatusCode = HttpStatusCode.Forbidden;
            throw new whttp.HttpResponseException(message1);
            
        }

        //
        // POST: /Account/ExternalLogin

        [HttpPost]
        [AllowAnonymous]
        public ActionResult ExternalLogin(string provider, string returnUrl)
        {
            return new ExternalLoginResult(provider, Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
        }

        //
        // GET: /Account/ExternalLoginCallback

        [AllowAnonymous]
        public ActionResult ExternalLoginCallback(string returnUrl)
        {
            AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
            if (!result.IsSuccessful)
            {
                return RedirectToAction("ExternalLoginFailure");
            }

            if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false))
            {
                return RedirectToLocal(returnUrl);
            }

            if (User.Identity.IsAuthenticated)
            {
                // If the current user is logged in add the new account
                OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, User.Identity.Name);
                return RedirectToLocal(returnUrl);
            }
            else
            {
                // User is new, ask for their desired membership name
                string loginData = OAuthWebSecurity.SerializeProviderUserId(result.Provider, result.ProviderUserId);
                ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(result.Provider).DisplayName;
                ViewBag.ReturnUrl = returnUrl;
                return View("ExternalLoginConfirmation", new RegisterExternalLoginModel { UserName = result.UserName, ExternalLoginData = loginData });
            }
        }

        //
        // POST: /Account/ExternalLoginConfirmation

        [HttpPost]
        [AllowAnonymous]
        public ActionResult ExternalLoginConfirmation(RegisterExternalLoginModel model, string returnUrl)
        {
            string provider = null;
            string providerUserId = null;

            if (User.Identity.IsAuthenticated || !OAuthWebSecurity.TryDeserializeProviderUserId(model.ExternalLoginData, out provider, out providerUserId))
            {
                return RedirectToAction("Manage");
            }

            if (ModelState.IsValid)
            {
                // Insert a new user into the database
                //using (UsersContext db = new UsersContext())
                //{
                    //UserProfile_ui user = db.UserProfiles.FirstOrDefault(u => u.UserName.ToLower() == model.UserName.ToLower());
                    // Check if user already exists
                    //if (user == null)
                    //{
                        // Insert name into the profile table
                        //db.UserProfiles.Add(new UserProfile_ui { UserName = model.UserName });
                        //db.SaveChanges();

                        OAuthWebSecurity.CreateOrUpdateAccount(provider, providerUserId, model.UserName);
                        OAuthWebSecurity.Login(provider, providerUserId, createPersistentCookie: false);

                        return RedirectToLocal(returnUrl);
                    //}
                    //else
                    //{
                    //    ModelState.AddModelError("UserName", "User name already exists. Please enter a different user name.");
                    //}
                //}
            }

            ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(provider).DisplayName;
            ViewBag.ReturnUrl = returnUrl;
            return View(model);
        }

        //
        // GET: /Account/ExternalLoginFailure

        [AllowAnonymous]
        public ActionResult ExternalLoginFailure()
        {
            return View();
        }

        [AllowAnonymous]
        [ChildActionOnly]
        public ActionResult ExternalLoginsList(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            return PartialView("_ExternalLoginsListPartial", OAuthWebSecurity.RegisteredClientData);
        }

        [ChildActionOnly]
        public ActionResult RemoveExternalLogins()
        {
            ICollection<OAuthAccount> accounts = OAuthWebSecurity.GetAccountsFromUserName(User.Identity.Name);
            List<ExternalLogin> externalLogins = new List<ExternalLogin>();
            foreach (OAuthAccount account in accounts)
            {
                AuthenticationClientData clientData = OAuthWebSecurity.GetOAuthClientData(account.Provider);

                externalLogins.Add(new ExternalLogin
                {
                    Provider = account.Provider,
                    ProviderDisplayName = clientData.DisplayName,
                    ProviderUserId = account.ProviderUserId,
                });
            }

            ViewBag.ShowRemoveButton = externalLogins.Count > 1 || OAuthWebSecurity.HasLocalAccount(WebMatrix.WebData.WebSecurity.GetUserId(User.Identity.Name));
            return PartialView("_RemoveExternalLoginsPartial", externalLogins);
        }

        [AllowAnonymous]
        [HttpGet]
        public ActionResult Confirm(string tk)
        {
            try
            {
                bool success = WebMatrix.WebData.WebSecurity.ConfirmAccount(tk);
                if (!success)
                {
                    ViewBag.Error = "Account has not been activated";
                    //return new HttpStatusCodeResult(403, "Account has not been activated");
                }
                return View();
            }
            catch (Exception ex)
            {

                MyLogger.MyLog("T2.Medussa", "MVC4", "Error Occurred in Confirm account activation", MyEnum.LogCategories.Error, 10, 7010, System.Diagnostics.TraceEventType.Information, ex.Message + ex.StackTrace);
                    HttpResponseMessage message = new HttpResponseMessage();
                    message.ReasonPhrase = "Error activating the account.";
                    message.StatusCode = HttpStatusCode.OK;
                    throw new whttp.HttpResponseException(message);
                
            }
        }

        [AllowAnonymous]
        [HttpGet]
        public ActionResult ConfirmSuccess()
        {
            return View();
        }

        [AllowAnonymous]
        [HttpGet]
        public ActionResult PasswordResetRequest()
        {
            return View();
        }

        [AllowAnonymous]
        [HttpPost]
        public ActionResult PasswordResetRequest(string UserName)
        {
            try
            {
                string passResetToken = WebMatrix.WebData.WebSecurity.GeneratePasswordResetToken(UserName);

                if (!passResetToken.Trim().Equals(""))
                {
                    MembershipUser user = Membership.GetUser(UserName);

                    //Send email activation link
                    EmailService emailService = new EmailService();
                    emailService.SendPasswordResetEmail(user.Email, passResetToken);
                }

                return new HttpStatusCodeResult(200, "Password reset link has been sent to your email.");
            }
            catch (Exception ex)
            {
                return new HttpStatusCodeResult(500, "Error Occurred in Passwordreset request");
            }
        }

        [AllowAnonymous]
        [HttpGet]
        public ActionResult PasswordReset(string tk)
        {
            TempData.Add("token", tk);
            return View();
        }

        [AllowAnonymous]
        [HttpPost]
        public ActionResult PasswordResetAction(string newPassword)
        {
            string token = HttpUtility.UrlDecode(TempData["token"].ToString());
            TempData.Remove("token");
            bool success = WebMatrix.WebData.WebSecurity.ResetPassword(token, newPassword);
            if (success)
                return Redirect("~/Account/ChangePasswordSuccess");
            else
                ViewBag.Message = "Password reset failed.";
            return View();
        }

        public ActionResult ChangePasswordSuccess()
        {
            return View();
        }

        [HttpPost]
        public ActionResult RemoveUserAccount(string userName)
        {
            try
            {
                if(userName.Equals(Membership.GetUser().UserName))
                {
                    return new HttpStatusCodeResult(500, "Own account cannot be removed");    
                }

                SimpleMembershipProvider membershipProvider = (SimpleMembershipProvider)(Membership.Provider);
                if (WebMatrix.WebData.WebSecurity.UserExists(userName))
                {
                    membershipProvider.DeleteUser(userName, true);
                }
                else
                    throw new Exception("User doesnot exist");

                return new HttpStatusCodeResult(200, "Account has been removed");
            }
            catch (Exception ex)
            {

                MyLogger.MyLog("T2.Medussa", "MVC4", "Error Occurred in Remove user account", MyEnum.LogCategories.Error, 10, 7010, System.Diagnostics.TraceEventType.Information, ex.Message + ex.StackTrace);
                HttpResponseMessage message = new HttpResponseMessage();
                message.ReasonPhrase = "Error removing the account.";
                message.StatusCode = HttpStatusCode.OK;
                throw new whttp.HttpResponseException(message);

            }
        }

        [AllowAnonymous]
        public JsonResult PasswordQuestions()
        {
            T2.WebSecurity.SimpleMembershipProvider membershipProvider = (T2.WebSecurity.SimpleMembershipProvider)(Membership.Provider);
            IList<IPasswordQuestion_biz> list = membershipProvider.GetPasswordQuestions();

            return Json(list, JsonRequestBehavior.AllowGet);
        }

        //[HttpPost]
        //public ActionResult SetApplication(int appId)
        //{
        //    HttpContext.Session["ApplicationId"] = appId;
        //    return Json(new {},  JsonRequestBehavior.AllowGet);
        //}

        #region Helpers
        private ActionResult RedirectToLocal(string returnUrl)
        {
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }

        public enum ManageMessageId
        {
            ChangePasswordSuccess,
            SetPasswordSuccess,
            RemoveLoginSuccess,
        }

        internal class ExternalLoginResult : ActionResult
        {
            public ExternalLoginResult(string provider, string returnUrl)
            {
                Provider = provider;
                ReturnUrl = returnUrl;
            }

            public string Provider { get; private set; }
            public string ReturnUrl { get; private set; }

            public override void ExecuteResult(ControllerContext context)
            {
                OAuthWebSecurity.RequestAuthentication(Provider, ReturnUrl);
            }
        }

        private static string ErrorCodeToString(MembershipCreateStatus createStatus)
        {
            // See http://go.microsoft.com/fwlink/?LinkID=177550 for
            // a full list of status codes.
            switch (createStatus)
            {
                case MembershipCreateStatus.DuplicateUserName:
                    return "User name already exists. Please enter a different user name.";

                case MembershipCreateStatus.DuplicateEmail:
                    return "A user name for that e-mail address already exists. Please enter a different e-mail address.";

                case MembershipCreateStatus.InvalidPassword:
                    return "The password provided is invalid. Please enter a valid password value.";

                case MembershipCreateStatus.InvalidEmail:
                    return "The e-mail address provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidAnswer:
                    return "The password retrieval answer provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidQuestion:
                    return "The password retrieval question provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidUserName:
                    return "The user name provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.ProviderError:
                    return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                case MembershipCreateStatus.UserRejected:
                    return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                default:
                    return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
            }
        }
        #endregion
    }
}
